package com.vcredit.bird.monitor;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class MonitorSystem implements MethodInterceptor, ApplicationContextAware {

	private Log log = LogFactory.getLog(MonitorSystem.class);

	private static ConcurrentHashMap<Class<?>, ExceptionHandlerProxy> monitorMap = new ConcurrentHashMap<Class<?>, ExceptionHandlerProxy>();

	@Autowired
	@Qualifier("defaultExceptionHandler")
	private MonitorExceptionHandler monitorExceptionHandler;

	@Autowired
	private SendMailMessage sendMailMessage;

	private SendSmsMessage sendSmsMessage;

	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		Object result = null;
		Method method = invocation.getMethod();
		Monitor monitor = method.getAnnotation(Monitor.class);
		String methodName = monitor.value();
		Class<?>[] handle = monitor.handle();
		try {
			result = invocation.proceed();
		} catch (Exception e) {
			log.error(e);
			for (Class<?> clazz : handle) {
				ExceptionHandlerProxy proxy = monitorMap.get(clazz);
				if (proxy != null) {
					proxy.handleException(methodName, e, method);
				}
			}
			throw e;
		}
		return result;
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		createSendType(applicationContext);
		Collection<MonitorExceptionHandler> handle = applicationContext.getBeansOfType(MonitorExceptionHandler.class)
				.values();
		if (handle.size() == 1) {
			createHandleProxy();
			return;
		}
		for (MonitorExceptionHandler obj : handle) {
			if (obj.getClass().isAnnotationPresent(MonitorAdvice.class)) {
				monitorExceptionHandler = obj;
				createHandleProxy();
			}
		}
	}

	public void createHandleProxy() {
		for (Method method : monitorExceptionHandler.getClass().getMethods()) {
			ExceptionHandler exceptionHandle = method.getAnnotation(ExceptionHandler.class);
			if (null != exceptionHandle) {
				Class<?> clazz = exceptionHandle.value()[0];
				MessageSendTypeEnum sendType = exceptionHandle.sendType();
				SendMonitorMessage sender = null;
				switch (sendType) {
				case Mail:
					sender = sendMailMessage;
					break;
				case SendSms:
					if (null != sendSmsMessage) {
						sender = sendSmsMessage;
					} else {
						sender = sendMailMessage;
					}
					break;
				default:
					sender = sendMailMessage;
					break;
				}
				ExceptionHandlerProxy proxy = new ExceptionHandlerProxy(monitorExceptionHandler, method, sender);
				monitorMap.put(clazz, proxy);
			}
		}
	}

	public void createSendType(ApplicationContext applicationContext) {
		Collection<SendMonitorMessage> sender = applicationContext.getBeansOfType(SendMonitorMessage.class).values();
		for (SendMonitorMessage obj : sender) {
			if (obj instanceof SendSmsMessage) {
				SendSmsMessage smsSender = (SendSmsMessage) obj;
				sendSmsMessage = smsSender;
			}
		}
	}
}
